package osp.Tasks;

import java.util.*;
import java.util.Vector;
import java.lang.Math;
import osp.IFLModules.*;
import osp.Threads.*;
import osp.Ports.*;
import osp.Memory.*;
import osp.FileSys.*;
import osp.Utilities.*;
import osp.Hardware.*;

/**
    The student module dealing with the creation and killing of
    tasks.  A task acts primarily as a container for threads and as
    a holder of resources.  Execution is associated entirely with
    threads.  The primary methods that the student will implement
    are do_create(TaskCB) and do_kill(TaskCB).  The student can choose
    how to keep track of which threads are part of a task.  In this
    implementation, an array is used.

    @OSPProject Tasks
*/
public class TaskCB extends IflTaskCB
{
	
	private ArrayList<ThreadCB> ListThreads;        // Lista de Threads
	private ArrayList<PortCB> ListPorts;            // Lista de Ports
	private ArrayList<OpenFile> ListOpenFiles;      // Lista de OpenFiles
	//private ThreadCB firstThread;
	
    /**
       The task constructor. Must have

       	   super();

       as its first statement.

       @OSPProject Tasks
    */
    public TaskCB()
    {
        // your code goes here
    	super();
    }

    /**
       This method is called once at the beginning of the
       simulation. Can be used to initialize static variables.

       @OSPProject Tasks
    */
    public static void init()
    {
        // your code goes here

    }

    /** 
        Sets the properties of a new task, passed as an argument. 
        
        Creates a new thread list, sets TaskLive status and creation time,
        creates and opens the task's swap file of the size equal to the size
	(in bytes) of the addressable virtual memory.

	@return task or null

        @OSPProject Tasks
    */
    static public TaskCB do_create()
    {
    	TaskCB newTask = new TaskCB();          // criando uma instância de uma tarefa
    	PageTable newPage = new PageTable(newTask);   // criando um PageTable
    	newTask.setPageTable(newPage);              // associando este PageTable a tarefa
    	
    	// Inicializando listas
    	newTask.ListThreads = new ArrayList<ThreadCB>();   // instanciando a lista de Threads
    	newTask.ListPorts = new ArrayList<PortCB>();       // instanciando a lista de Ports
    	newTask.ListOpenFiles = new ArrayList<OpenFile>(); // instanciando a lista de OpenFiles
    	
    	newTask.setCreationTime(HClock.get());       // setando o horario de criacao da tarefa
    	newTask.setStatus(TaskLive);       // setando estado de tarefa como ativa
    	newTask.setPriority(1);            //setando prioridade como 1 
    	
    	String pathname = new String(SwapDeviceMountPoint+"/"+String.valueOf(newTask.getID())); // //montando o endereco da SwapFile
    	
    	FileSys.create(pathname, (int)Math.pow(2, MMU.getVirtualAddressBits())); //criando a SwapFile
    	
 		
    		
    	OpenFile newopenfile = OpenFile.open(pathname, newTask); //Abriando a SwapFile
       	newTask.setSwapFile(newopenfile);   //Associando a SwapFile a Tarefa

    	if(newopenfile == null) //Se não conseguiu abrir
    	{
    		ThreadCB.dispatch();
    		newTask.do_kill();
    		return null;
    	}
    	
     	

    	
    	ThreadCB.create(newTask);  //Criando a primeira Thread

    	
    	return newTask;
    }

    /**
       Kills the specified task and all of it threads. 

       Sets the status TaskTerm, frees all memory frames 
       (reserved frames may not be unreserved, but must be marked 
       free), deletes the task's swap file.
	
       @OSPProject Tasks
    */
    public void do_kill()
    {
    	
    	this.setStatus(TaskTerm);  //setando estado terminal pra Tarefa
    	
    	// killing all the threads
      	for (int i = this.ListThreads.size() -1; i >=0 ; i--) { 
    		this.ListThreads.get(i).kill();  
    	}
    	
    	// destroying all ports
    	for (int i = this.ListPorts.size() -1; i >=0 ; i--) {
    		this.ListPorts.get(i).destroy();
    		//this.ListPorts.remove(i);
    	}
    	
        //closing openFiles
    	for (int i = this.ListOpenFiles.size()-1; i >=0 ; i--) {
    		this.ListOpenFiles.get(i).close();

    	}
   	
    	// deallocate memory
    	PageTable pagetable = this.getPageTable();
    	pagetable.deallocateMemory();
    	
    
    	
    	
    
    	
   	// Delete Swap File
    	String pathname = new String(SwapDeviceMountPoint+"/"+String.valueOf(this.getID())); 
    	FileSys.delete(pathname);  
    }

    /** 
	Returns a count of the number of threads in this task. 
	
	@OSPProject Tasks
    */
    public int do_getThreadCount()
    {

    	return this.ListThreads.size();
    }

    /**
       Adds the specified thread to this task. 
       @return FAILURE, if the number of threads exceeds MaxThreadsPerTask;
       SUCCESS otherwise.
       
       @OSPProject Tasks
    */
    public int do_addThread(ThreadCB thread)
    {    	
    	if (this.do_getThreadCount() >= ThreadCB.MaxThreadsPerTask) {
    		return this.FAILURE; 
    	}
    	else {
    		this.ListThreads.add(thread);
    		return this.SUCCESS;
    	}
    }

    /**
       Removes the specified thread from this task. 		

       @OSPProject Tasks
    */
    public int do_removeThread(ThreadCB thread)
    {
    	if (this.ListThreads.contains(thread)) {
    		this.ListThreads.remove(thread);
    		return this.SUCCESS;
    	}
    	else {
    		return this.FAILURE;
    	}
    }

    /**
       Return number of ports currently owned by this task. 

       @OSPProject Tasks
    */
    public int do_getPortCount()
    {
    	return this.ListPorts.size();
    }

    /**
       Add the port to the list of ports owned by this task.
	
       @OSPProject Tasks 
    */ 
    public int do_addPort(PortCB newPort)
    {
    	if (this.do_getPortCount() >= PortCB.MaxPortsPerTask) {
    		return this.FAILURE;
    	}
    	else {
    		this.ListPorts.add(newPort);
    		return this.SUCCESS;
    	}
    }

    /**
       Remove the port from the list of ports owned by this task.

       @OSPProject Tasks 
    */ 
    public int do_removePort(PortCB oldPort)
    {
    	if (this.ListPorts.contains(oldPort)) {
    		this.ListPorts.remove(oldPort);
    		return this.SUCCESS;
    	}
    	else {
    		return this.FAILURE;
    	}
    }

    /**
       Insert file into the open files table of the task.

       @OSPProject Tasks
    */
    public void do_addFile(OpenFile file)
    {
        this.ListOpenFiles.add(file);
    }

    /** 
	Remove file from the task's open files table.

	@OSPProject Tasks
    */
    public int do_removeFile(OpenFile file)
    {
    	if (this.ListOpenFiles.contains(file)) {
    		this.ListOpenFiles.remove(file);
    		return this.SUCCESS;
    	}
    	else {
    		return this.FAILURE;
    	}
    }

    /**
       Called by OSP after printing an error message. The student can
       insert code here to print various tables and data structures
       in their state just after the error happened.  The body can be
       left empty, if this feature is not used.
       
       @OSPProject Tasks
    */
    public static void atError()
    {
    //	for (int i = 0; i < this.ListThreads.size(); i++) {
    //		System.out.println(this.ListThreads.get(i).toString());
    //	}
    }	

    /**
       Called by OSP after printing a warning message. The student
       can insert code here to print various tables and data
       structures in their state just after the warning happened.
       The body can be left empty, if this feature is not used.
       
       @OSPProject Tasks
    */
    public static void atWarning()
    {
        // your code goes here

    }


    /*
       Feel free to add methods/fields to improve the readability of your code
    */

}

/*
      Feel free to add local classes to improve the readability of your code
*/
